EDA: Caracterización y distribución de los datos#

Líbrerias y modulos necesarios#

import json
import warnings
import pandas as pd
import plotly.express as px
warnings.filterwarnings('ignore')
import plotly.graph_objects as go
from urllib.request import urlopen
from funciones import *

Conjunto de datos#

url = r"https://github.com/sePerezAlbor/Data/blob/main/data_to_eda.xlsx?raw=true"
data = pd.read_excel(url, na_values = [" "])
data.head()
region_Def Nombre_Departamento_Def Nombre_Municipio_Def Nombre_Departamento_Res Nombre_Municipio_Res sitio_def año_def mes_def sexo estado_civil grupo_edad nivel_edu seg_social asistencia_med causa_basica
0 Orinoquía META VILLAVICENCIO META VILLAVICENCIO Casa 1985 Enero Femenino Casado 45-49 años No especificado Otro NO Parte no especificada
1 Andina BOYACÁ VENTAQUEMADA BOYACÁ VENTAQUEMADA Casa 1985 Enero Femenino Soltero 85+ años No especificado Otro NO Parte no especificada
2 Orinoquía META PUERTO LÓPEZ META PUERTO LÓPEZ Casa 1985 Enero Femenino Casado 50-54 años No especificado Otro NO Parte no especificada
3 Andina HUILA NEIVA HUILA NEIVA Hospital o Clínica 1985 Enero Femenino Casado 35-39 años No especificado Otro NO Parte no especificada
4 Andina HUILA NEIVA HUILA NEIVA Casa 1985 Enero Femenino Casado 75-79 años No especificado Otro NO Parte no especificada
data.shape
(78479, 15)

Filtración conjunto de datos#

Antes de realizar el respectivo análisis descriptivo del conjunto de datos. Se filtrará este mismo solo para incluir mujeres mayores de 40 años.

data['grupo_edad'].value_counts()
grupo_edad
55-59 años          9373
60-64 años          9309
50-54 años          8708
65-69 años          8291
70-74 años          7473
45-49 años          6927
75-79 años          6151
80-84 años          4969
40-44 años          4880
85+ años            4554
Edad desconocida    3260
35-39 años          2777
30-34 años          1333
25-29 años           363
20-24 años            91
15-19 años            20
Name: count, dtype: int64
grupos_mayores_40 = [
    '40-44 años', '45-49 años', '50-54 años', '55-59 años',
    '60-64 años', '65-69 años', '70-74 años', '75-79 años',
    '80-84 años', '85+ años'
]

data_mayores_40 = data[data['grupo_edad'].isin(grupos_mayores_40)]
data = data_mayores_40.copy()
# Diccionario de mapeo
grupo_mapeo = {
    '40-44 años': '40-54 años',
    '45-49 años': '40-54 años',
    '50-54 años': '40-54 años',
    '55-59 años': '55-64 años',
    '60-64 años': '55-64 años',
    '65-69 años': '65-74 años',
    '70-74 años': '65-74 años',
    '75-79 años': '75+ años',
    '80-84 años': '75+ años',
    '85+ años':  '75+ años'
}

# Crear una nueva columna con los grupos
data['grupo_edad'] = data['grupo_edad'].map(grupo_mapeo)
data.shape
(70635, 15)

También se filtrará el conjunto de datos por los últimos tres quinquenios con los que se cuenta (2009 - 2023)

data_filtrada = data[(data['año_def']>= 2009) & (data['año_def']<= 2023)]
data_filtrada.shape
(41610, 15)
data = data_filtrada.copy()

Análisis general del conjunto de datos#

# Dividir en dos grupos (ajusta según necesidad)
categorical_columns = data.select_dtypes(include='object').columns
grupo1 = categorical_columns[:12]  # Primeras 12 columnas
display(data[grupo1].describe())
region_Def Nombre_Departamento_Def Nombre_Municipio_Def Nombre_Departamento_Res Nombre_Municipio_Res sitio_def mes_def sexo estado_civil grupo_edad nivel_edu seg_social
count 41610 41610 41610 41610 41610 41610 41610 41610 41610 41610 41610 41610
unique 5 33 841 33 944 3 12 1 4 4 6 5
top Andina BOGOTA DC BOGOTA DC BOGOTA DC BOGOTA DC Hospital o Clínica Diciembre Femenino Unión Libre, Divorciado/Otro 55-64 años Primaria Contributivo
freq 24900 8263 8263 6954 7437 28549 3692 41610 11647 11157 15416 21075

Veamos la cantidad de datos faltantes en general.

grafico_datos_faltantes(data)

Análisis Univariado#

Variable: Región de defunción#

grafico_circular_categorico(data, 'region_Def', 'Distribución de Defunciones por Región')
grafico_barras_categoricas(data, 'region_Def', 'Distribución de Defunciones por Región', 'Defunciones', 'Número de Defunciones')

Variable: Departamento de defunción#

top_10_departamentos = data['Nombre_Departamento_Def'].value_counts().head(10).index

# Filtrar el DataFrame original para que solo contenga esos departamentos
data_top10 = data[data['Nombre_Departamento_Def'].isin(top_10_departamentos)]

# Llamar la función original con el nombre de la columna
grafico_barras_categoricas(data_top10, 'Nombre_Departamento_Def', 'Departamento', 'Cantidad de defunciones', 'Defunciones por departamento (Top 10)')
bottom_10_departamentos = data['Nombre_Departamento_Def'].value_counts().tail(10).index
data_bottom10 = data[data['Nombre_Departamento_Def'].isin(bottom_10_departamentos)]
# Reemplazar el nombre largo por uno más corto en una copia del DataFrame
data_bottom10['Nombre_Departamento_Def'] = data_bottom10['Nombre_Departamento_Def'].replace(
    'ARCHIPIÉLAGO DE SAN ANDRÉS Y PROVIDENCIA Y SANTA CATALINA',
    'SAN ANDRÉS'
)
grafico_barras_categoricas(data_bottom10, 'Nombre_Departamento_Def', 'Departamento', 'Cantidad de defunciones', 'Defunciones por departamento (Bottom 10)')

Variable: Municipio#

top_10_municipios = data['Nombre_Municipio_Def'].value_counts().head(10).index
data_top10_mun = data[data['Nombre_Municipio_Def'].isin(top_10_municipios)]

grafico_barras_categoricas(data_top10_mun, 'Nombre_Municipio_Def', 'Municipio', 'Cantidad de defunciones', 'Defunciones por municipio (Top 10)')

Variable: Sitio de defunción#

grafico_barras_categoricas(data, 'sitio_def', 'Sitio de defunción', 'Cantidad de registros', 'Distribución sitio de defunción')
grafico_circular_categorico(data, 'sitio_def', 'Distribución de Defunciones por Sitio de Defunción')

Variable: Estado civil#

grafico_barras_categoricas(data, 'estado_civil', 'Estado civil', 'Cantidad de registros', 'Distribución estado civil')
grafico_circular_categorico(data, 'estado_civil', 'Distribución de Defunciones por Estado Civil')

Variable: Grupo de edad#

grafico_edad(data,  'Grupo de edad', 'Cantidad de registros', 'Distribución de Defunciones por Edad')
grafico_barras_categoricas(data, 'grupo_edad', 'Grupo de edad', 'Cantidad de registros', 'Distribución por grupo de edad')

Variable: Nivel educativo#

grafico_barras_categoricas(data, 'nivel_edu', 'Nivel educativo', 'Cantidad de registros', 'Distribución nivel educativo')
grafico_circular_categorico(data, 'nivel_edu', 'Distribución de Defunciones por Nivel Educativo')

Variable: Seguridad social#

grafico_barras_categoricas(data,'seg_social', 'Seguridad social', 'Cantidad de registros', 'Distribución seguridad social')

Variable: Asistencia médica#

grafico_barras_categoricas(data, 'asistencia_med', 'Asistencia médica', 'Cantidad de registros', 'Distribución asistencia médica')
grafico_circular_categorico(data, 'asistencia_med', 'Distribución de Defunciones por Asistencia Médica')

Variable: Causa básica#

grafico_barras_categoricas(data, 'causa_basica', 'Causa básica', 'Cantidad de registros', 'Distribución causa básica')

Variable: Mes de defunción#

grafico_mes_def(data, 'Mes de defunción', 'Cantidad de registros', 'Distribución de Defunciones por Mes')
grafico_barras_categoricas(data, 'mes_def', 'Mes de defunción', 'Cantidad de registros', 'Distribución de Defunciones por Mes de mayor a menor')

Análisis Bivariado#

Tendencia de defunciones por año y región#

data_grouped = data.groupby(['año_def', 'region_Def']).size().reset_index(name='Defunciones')

color_palette = ["#ffb6c1", "#ff69b4", "#db7093", "#c71585", 
                 "#ff1493", "#e60073", "#cc0033", "#b20000"]

fig6 = px.line(data_grouped, 
               x='año_def', 
               y='Defunciones', 
               color='region_Def',
               title='Tendencia de Defunciones por Año y Región',
               markers=True,  # Agregar marcadores
               color_discrete_sequence=color_palette)  # Aplicar paleta personalizada

fig6.update_layout(
    font=dict(family="Bahnschrift", color="black"),  # Fuente Bahnschrift en negro
    xaxis_title="Año de Defunción", 
    yaxis_title="Número de Defunciones",
    legend_title="Región",  # Título de la leyenda
    template="plotly_white"  # Fondo blanco para mayor claridad
)

fig6.show()

Evolución de las Defunciones según el Lugar de Muerte#

# Agrupar datos por año de defunción y sitio de defunción
data_grouped = data.groupby(["año_def", "sitio_def"]).size().reset_index(name="count")

# Gráfico de líneas con tonalidades rosadas
fig = px.line(data_grouped, 
              x="año_def", 
              y="count", 
              color="sitio_def",
              markers=True,  # Agregar puntos en la línea
              title="Evolución de las Defunciones según el Lugar de Muerte",
              color_discrete_sequence=["#ffb6c1", "#ff69b4", "#db7093", "#c71585"])  # Tonos rosados

fig.update_layout(
    font=dict(family="Bahnschrift", color="black"),
    xaxis_title="Año de Defunción", 
    yaxis_title="Número de Defunciones",
    legend_title="Lugar de Defunción",
    template="plotly_white"
)

fig.show()

Evolución de Defunciones por Grupo de Edad#

data_grouped = data.groupby(["año_def", "grupo_edad"]).size().reset_index(name="count")
color_palette = ["#ffb6c1", "#ff69b4", "#db7093", "#c71585", 
                 "#ff1493", "#e60073", "#cc0033", "#b20000", 
                 "#990000", "#800000", "#ff3366", "#ff0033", 
                 "#d40000", "#a10000"]
fig = px.line(data_grouped, 
              x="año_def", 
              y="count", 
              color="grupo_edad", 
              title="Evolución de Defunciones por Grupo de Edad",
              markers=True,  # Agregar puntos en la línea
              color_discrete_sequence=color_palette,  # Aplicar paleta de colores
              labels={"grupo_edad": "Grupo de Edad", "año_def": "Año", "count": "Número de Defunciones"})  

fig.update_layout(
    font=dict(family="Bahnschrift", color="black"),  # Fuente en negro
    xaxis_title="Año de Defunción", 
    yaxis_title="Número de Defunciones",
    legend_title="Grupo de Edad",  
    width=800,  
    height=600,     
    template="plotly_white"
)

fig.show()

Análisis de los casos a lo largo del tiempo#

meses_map = {
    "Enero": "01", "Febrero": "02", "Marzo": "03", "Abril": "04", "Mayo": "05", "Junio": "06",
    "Julio": "07", "Agosto": "08", "Septiembre": "09", "Octubre": "10", "Noviembre": "11", "Diciembre": "12"
}

data['mes_def'] = data['mes_def'].map(meses_map)
data['Fecha'] = data['año_def'].astype(str) + '-' + data['mes_def']

conteo_fechas = data['Fecha'].value_counts().reset_index()
conteo_fechas.columns = ['Fecha', 'Cantidad']
conteo_fechas = conteo_fechas.sort_values('Fecha')
conteo_fechas['Fecha'] = pd.to_datetime(conteo_fechas['Fecha'], format='%Y-%m')

fig = px.line(conteo_fechas, 
              x='Fecha', 
              y='Cantidad',
              title="Casos de Defunción a lo Largo del Tiempo",
              labels={'Cantidad': 'Número de Defunciones', 'Fecha': 'Fecha'},
              markers=True)

fig.update_traces(line=dict(color="#C2185B"),  
                  marker=dict(color="#F06292", size=8))  

fig.update_layout(
    template="plotly_white",
    xaxis_title="Fecha",
    yaxis_title="Número de Defunciones",
    font=dict(family="Bahnschrift", size=14, color="black"),
    title_font=dict(family="Bahnschrift", size=22, color="black"),
    plot_bgcolor="white",
    paper_bgcolor="white"
)

fig.show()

Mapa de casos por departamento#

with urlopen('https://raw.githubusercontent.com/lihkir/Uninorte/main/AppliedStatisticMS/DataVisualizationRPython/Lectures/Python/PythonDataSets/Colombia.geo.json') as response:
    counties = json.load(response)
    
locs=[]; z_id=[];
for loc in counties['features']:
    loc['id'] = loc['properties']['NOMBRE_DPT']
    locs.append(loc['properties']['NOMBRE_DPT'])
    z_id.append(loc['properties']['HECTARES'])
colorscale = [
    [0, "#fde0dc"],  # Rosa claro
    [0.2, "#f9bdbb"],  
    [0.4, "#f69988"],  
    [0.6, "#f36c60"],  
    [0.8, "#e84e40"],  
    [1, "#d50000"]  # Rojo intenso
]

# Contar la cantidad de casos por departamento
casos_por_departamento = data['Nombre_Departamento_Def'].value_counts().reset_index()
casos_por_departamento.columns = ['Nombre_Departamento_Def', 'Casos']

# Reemplazar nombres para que coincidan con el GeoJSON
correccion_nombres = {
    'ATLÁNTICO': 'ATLANTICO',
    'BOLÍVAR': 'BOLIVAR',
    'BOGOTA DC': 'SANTAFE DE BOGOTA D.C',
    'BOYACÁ': 'BOYACA',
    'CÓRDOBA': 'CORDOBA',
    'CAQUETÁ': 'CAQUETA',
    'CHOCÓ': 'CHOCO',
    'LA GUAJIRA': 'LA GUAJIRA',
    'ARCHIPIÉLAGO DE SAN ANDRÉS Y PROVIDENCIA Y SANTA CATALINA': 'ARCHIPIELAGO DE SAN ANDRES PROVIDENCIA Y SANTA CATALINA',
    'GUAINÍA': 'GUAINIA',
    'GUAVIARE': 'GUAVIARE',
    'VAUPÉS': 'VAUPES',
    'VICHADA': 'VICHADA'
}

casos_por_departamento['Nombre_Departamento_Def'] = casos_por_departamento['Nombre_Departamento_Def'].replace(correccion_nombres)

locs = casos_por_departamento['Nombre_Departamento_Def'].tolist()
z_id = casos_por_departamento['Casos'].tolist()

fig = go.Figure(go.Choroplethmapbox(
                    geojson=counties,
                    locations=locs,
                    z=z_id,
                    colorscale= colorscale,
                    colorbar_title="Casos de Defunción"))

fig.update_layout(mapbox_style="open-street-map",
                  mapbox_zoom=4,
                  mapbox_center={"lat": 4.570868, "lon": -74.2973328}  ,width=800,  
    height=600 )

fig.show()
data.to_csv('../data_to_models.csv')